import './main.scss';

import { ref, reactive, watch, computed, PropType, defineComponent, onMounted, nextTick } from 'vue';

import {
    ElTable,
    ElTableColumn,
    ElPagination,
    ElTag,
    ElTooltip,
    ElButton,
    ElRow,
    ElDropdown,
    ElDropdownMenu,
    ElDropdownItem,
    ElPopover,
    ElRadio,
    ElCheckboxGroup,
    ElCheckbox
} from 'element-plus';

import { createComponentName, ComponentInstance, type, useExpose } from '../utils';
import { get as _get } from 'lodash';

import emptyImage from '@/assets/noData.png';
import { tableH, reSize, getRandomKey } from '@/utils/utils';
import { startLoading, endLoading } from '@/utils/request';

const [name, bem] = createComponentName('table');

export default defineComponent({
    name,
    props: {
        rowIdKey: {
            type: String,
            default: 'id'
        },
        showPagination: {
            type: Boolean,
            default: true
        },
        multiple: {
            type: Boolean,
            default: true
        },
        rowKey: {
            type: [Function, String],
            default: ''
        },
        option: {
            type: Array as PropType<any[]>,
            default: () => []
        },
        selectedIds: {
            type: Array as PropType<any[]>,
            default: () => []
        },
        fetchParams: {
            // 请求入参
            type: Object as PropType<Record<string, any>>,
            default: () => ({})
        },
        fetchApi: {
            type: [Function, Promise],
            default: null
        },
        fetchDataFormat: Function, // 表格数据转换
        dataProp: {
            // 后台接口返回的 data 为对象时总数、列表字段对应的 key，默认 tobal、rows
            type: Object as PropType<Record<string, string>>,
            default: () => ({ total: 'total', rows: 'rows' })
        },
        paramProp: {
            type: Object as PropType<Record<string, string>>,
            default: () => ({ page: 'page', pageSize: 'page_size' })
        },
        pageSizes: {
            type: Array as PropType<number[]>,
            default: () => [15, 25, 50, 100]
        },
        tabH: {
            type: String,
            default: ''
        },
        isLimitHeight: {
            type: Boolean,
            default: false
        },
        emptyText: {
            type: String,
            default: '暂无数据'
        },
        selectAllGroup: {
            type: Boolean,
            default: false
        },
        // true-table选中项回显，使用props.selectedIds数据
        echoType: {
            type: Boolean,
            default: false
        },
        closeLoading: {
            type: Boolean,
            default: false
        },
    },

    emit: [
        'updateData',
        'select',
        'selectionChange',
        'selectAll',
        'currentChange',
        'sizeChange',
        'dataChange',
        'radioChange',
        'sortChange'
    ],

    setup(props, { emit, slots }) {
        const tableRef = ref<ComponentInstance>();

        const randomKey = ref<string>('');
        randomKey.value = getRandomKey();

        const isAll = ref<boolean>(false);
        //全选已经选择的数据
        const selectTableTotal = reactive({
            value: 0
        });

        const isSelectAll = ref(false);

        const state: any = reactive({
            list: [],
            page: 1,
            page_size: props.fetchParams.page_size || 15,
            total: 0,
            selectionSize: 0,
            selectionIds: [],
            tooltips: false,
            tabH: '500px',
            reverseSelectionList: []
        });

        watch(
            () => props.fetchParams,
            () => {
                state.page = 1;
                getData();
            },
            { deep: true }
        );

        watch(
            () => isSelectAll.value,
            async val => {
                // 清空当前选择项
                tableRef.value?.clearSelection();
                if (isSelectAll.value) {
                    // 如勾选全选，则需要重新调用列表接口，清空已选择项
                    state.reverseSelectionList = []; // 清空反选项
                    state.page = 1;
                    await getData();
                    selectTableTotal.value = state.total;
                    emit('selectAllTotal', state.total);
                } else {
                    // 前端清空选择项。往外抛出选择项变更的事件
                    emit('selectAllTotal', 0);
                    selectTableTotal.value = 0;
                }
            },
            { deep: true }
        );

        watch(
            () => state.list,
            (val: any) => {
                emit('updateData', val);
            },
            { deep: true }
        );

        watch(
            () => isAll.value,
            (val: any) => {
                if (val) {
                    isSelectAll.value = true;
                } else {
                    isSelectAll.value = false;
                    state.reverseSelectionList = [];
                    emit('selectAllChange', state.reverseSelectionList);
                }
            }
        );

        const getData = async () => {
            if (!type.isFunction(props.fetchApi) && !type.isPromise(props.fetchApi)) return;
            props.closeLoading && startLoading();
            try {
                const { page, page_size } = state;

                let params = {
                    ...props.fetchParams,
                    [props.paramProp.page || 'page']: page,
                    [props.paramProp.pageSize || 'page_size']: page_size
                };

                let fetchApi: any = props.fetchApi;

                if (type.isPromise(fetchApi)) fetchApi = await fetchApi;

                if (type.isArray(fetchApi)) {
                    const [extra, fetch] = fetchApi;
                    params = { ...params, ...extra };
                    fetchApi = fetch;
                }

                // const { data, total } = (await fetchApi?.(params)) ?? {}
                const res = (await fetchApi?.(params)) ?? {};
                emit('dataChange', res);

                const { data, total } = res;

                if (Array.isArray(data)) {
                    state.list = typeof props.fetchDataFormat === 'function' ? props.fetchDataFormat(data) ?? [] : data;
                    state.total = total ?? data.length;
                } else if (Reflect.has(data ?? {}, props.dataProp.rows)) {
                    const list = data[props.dataProp.rows] ?? [];
                    state.list = typeof props.fetchDataFormat === 'function' ? props.fetchDataFormat(list) ?? [] : list;
                    state.total = data[props.dataProp.total] ?? total ?? 0;
                }
                initTableData();
            } catch (err) {
                // eslint-disable-next-line no-console
                console.log('err: ', err);
            } finally {
                props.closeLoading && endLoading()

            }
        };

        // 勾选 Checkbox
        const onSelect = (selection: any, row: any) => {
            console.log('onSelect', selection);
            selection = selection.reduce((pre: any, cur: any) => {
                if (!pre.some((ite: any) => ite.id === cur.id)) {
                    pre.push(cur);
                }
                return pre;
            }, []);
            state.selectionIds = selection.map((item: any) => item.id);
            if (isSelectAll.value) {
                const index = state.reverseSelectionList.findIndex((el: any) => el == row.id);
                if (index > -1) {
                    state.reverseSelectionList.splice(index, 1);
                } else {
                    state.reverseSelectionList.push(row.id);
                }
                emit('selectAllChange', state.reverseSelectionList);
                emit('selectAllTotal', state.total - state.reverseSelectionList.length);
                selectTableTotal.value = state.total - state.reverseSelectionList.length;
            } else {
                emit('select', selection, row);
                emit('selectAllTotal', selection.length);
                selectTableTotal.value = selection.length;
            }
        };

        // 选择项变更
        const onSelectionChange = (selection: any) => {
            selection = selection.reduce((pre: any, cur: any) => {
                if (!pre.some((ite: any) => ite.id === cur.id)) {
                    pre.push(cur);
                }
                return pre;
            }, []);
            if (!props.multiple) {
                if (selection.length > 1) {
                    tableRef.value?.clearSelection();
                    tableRef.value?.toggleRowSelection(selection.pop());
                }
            }
            state.selectionSize = selection.length;
            emit('selectionChange', selection);
        };
        // 清空选择项
        const clearSelection = () => {
            tableRef.value?.clearSelection();
            // todo: 把已经选中的数组清空掉
            emit('selectAll', []);
        };
        // 全选 Checkbox
        const onSelectAll = (selection: any) => {
            if (isSelectAll.value) {
                if (selection.length) {
                    const ary: any = [];
                    state.reverseSelectionList.map((id: any) => {
                        if (selection.every((el: any) => el.id != id)) {
                            ary.push(id);
                        }
                    });
                    state.reverseSelectionList = ary;
                } else {
                    state.list.map((el: any) => {
                        state.reverseSelectionList.push(el.id);
                    });
                }
                emit('selectAllChange', state.reverseSelectionList);
                emit('selectAllTotal', state.total - state.reverseSelectionList.length);
                selectTableTotal.value = state.total - state.reverseSelectionList.length;
            } else {
                emit('selectAllTotal', selection.length);
                selectTableTotal.value = selection.length;
            }
            state.selectionSize = selection.length;
            emit('selectAll', selection);
        };

        // 切换页码
        const onCurrentChange = async (val: number) => {
            state.page = val;
            emit('currentChange', val);
            await getData();
        };

        // 切换表格长度
        const onSizeChange = async (val: number) => {
            state.page = 1;
            state.page_size = val;
            emit('sizeChange', val);
            await getData();
        };

        const onCellMouseEnter = (row: any, column: any, cell: any) => {
            if (!cell.classList.contains(bem('tow-line'))) return;
            const index = state.list.findIndex((item: any) => row.id === item.id);
            const cellHtml = cell.querySelector('.cell') as HTMLDivElement;
            const range = document.createRange();
            range.setStart(cellHtml, 0);
            range.setEnd(cellHtml, cellHtml.childNodes.length);
            const rangeHeight = ~~range.getBoundingClientRect().height;
            const padding =
                (parseInt(window.getComputedStyle(cellHtml).paddingTop, 10) || 0) +
                (parseInt(window.getComputedStyle(cellHtml).paddingBottom, 10) || 0);
            if (rangeHeight + padding > cellHtml.offsetHeight || cellHtml.scrollHeight > cellHtml.offsetHeight) {
                state.tooltips = false;
            } else {
                state.tooltips = true;
            }
        };

        const onCellMouseLeave = () => { };

        const onSortChange = (option: any) => emit('sortChange', option);

        // 获取表格实例
        const getTable = () => tableRef.value;

        // 获取是否全选
        const getIsSelectAll = () => isSelectAll.value;

        // 获取表格数据
        const getTableData = () => state.list;

        // 获取表格数据总条数
        const getTableTotal = () => state.total;

        //清空全选方法，配合全选使用
        const clearAllSelect = () => {
            isAll.value = false;
            isSelectAll.value = false;
            initTableData();
            selectTableTotal.value = 0;
            emit('selectAllTotal', 0);
        };

        // 表格重载，调用 fetch-api 接口，参数不变重新请求
        const reload = async () => {
            await getData();
        };
        const getTableH = (val = 0) => {
            const h = parseInt(tableH('.zw-table__com_table'));
            state.tabH = h - val + 'px';
        };

        const initTableData = () => {
            nextTick(() => {
                if (isSelectAll.value) {
                    tableRef.value?.clearSelection();
                    state.list.forEach((row: any) => {
                        row['id'] = row[props.rowIdKey];
                        if (state.reverseSelectionList.some((id: any) => id == row.id)) {
                            tableRef.value?.toggleRowSelection(row, false);
                        } else {
                            tableRef.value?.toggleRowSelection(row, true);
                        }
                    });
                } else {
                    state.list.forEach((row: any) => {
                        row['id'] = row[props.rowIdKey];
                        if (props.echoType && props.selectedIds.length && props.selectedIds.includes(row.id)) {
                            tableRef.value?.toggleRowSelection(row, true);
                        } else if (props.selectedIds.length && state.selectionIds.includes(row.id)) {
                            tableRef.value?.toggleRowSelection(row, true);
                        }
                    });
                }
            });
        };

        onMounted(async () => {
            await getData();
            state.selectionIds = props.selectedIds;
            if (!props.multiple) {
                if (state.selectionIds.length) {
                    radioModel.value = state.selectionIds[0];
                    const row = state.list.find((item: any) => item.id === state.selectionIds[0]);
                    !!row && radioClick(row);
                }
            }
            if (!props.isLimitHeight) {
                getTableH();
                reSize(() => getTableH());
            }
        });

        const doLayout = () => {
            nextTick(() => {
                getTable()?.doLayout();
            });
        };
        // 回显单选
        const echoRadio = (data: any) => {
            radioModel.value = data;
        };
        const setIsAll = () => {
            isAll.value = true;
        };

        useExpose({
            getTable,
            getTableData,
            getTableTotal,
            reload,
            getTableH,
            doLayout,
            clearSelection,
            echoRadio,
            getIsSelectAll,
            clearAllSelect,
            setIsAll
        });

        // 渲染顶部操作
        const renderHeaderOperate = () => {
            return (
                <ElRow class={bem('card-header')}>
                    <ElRow class={bem('card-header-left')}>
                        {props.selectAllGroup ? (
                            <div class={bem('header_o_l')}>
                                <ElCheckbox vModel={isAll.value}>全选</ElCheckbox>
                                &nbsp; 已选择{selectTableTotal.value}条数据
                            </div>
                        ) : (
                            ''
                        )}
                        {slots.headerOperateLeft ? slots.headerOperateLeft?.() : null}
                    </ElRow>
                    <ElRow>{slots.headerOperate?.()}</ElRow>
                </ElRow>
            );
        };

        // 渲染 tag
        const renderTags = ({ row, column, index, prop }: any) => {
            const tags = _get(row, prop, [])?.length ? _get(row, prop, []) : row.tags || [];
            if (tags?.length != 0) {
                return (
                    <div class={bem('tag-list')}>
                        <ElPopover
                            width={400}
                            trigger="hover"
                            popper-class="popper zw-el-tags auto_width tags_auto_height"
                            hide-after={0}
                            placement="top-start"
                            show-arrow={false}>
                            {{
                                reference: () => (
                                    <div class="zw-text-ellipsis">
                                        {tags.map((tag: any) => (
                                            <ElTag key={tag.id}>{tag.name}</ElTag>
                                        ))}
                                    </div>
                                ),
                                default: () => tags.map((tag: any) => <ElTag key={tag.id}>{tag.name}</ElTag>)
                            }}
                        </ElPopover>
                    </div>
                );
            } else {
                return <div>--</div>;
            }
        };
        // 渲染单选按钮
        const radioModel = ref();
        const radioClick = row => {
            emit('radioChange', row);
        };
        const renderRadio = ({ row, index, item }: any) => {
            return (
                <ElRadio vModel={radioModel.value} label={row.id} onChange={() => radioClick(row)}>
                    <span style="font-size: 0">''</span>
                </ElRadio>
            );
        };
        // 渲染 tooltips
        const renderShowOverflowTooltip = ({ row, index, item }: any) => {
            const isEmptyValue = (value: any) => (['', null, undefined].includes(value) ? '--' : value);
            return (
                <ElTooltip
                    effect="dark"
                    placement="top"
                    show-after={800}
                    content={row[item.prop]}
                    disabled={state.tooltips}
                    popper-class="auto_width auto_height">
                    <div class={bem('tooltip')}>{isEmptyValue(row[item.prop])}</div>
                </ElTooltip>
            );
        };
        // 渲染操作按钮
        const renderOperate = ({ row, column, index, option, renderOperate }: any) => {
            let options = null;

            if (renderOperate && typeof renderOperate === 'function') {
                options = renderOperate({ row, column, index });
            } else {
                if (typeof option === 'function') option = option({ row, column, index }) ?? null;
                if (!Array.isArray(option) || !option.length) return null;

                const optionIsNotRender = (item: any) => {
                    if (typeof item.isRender === 'function' || typeof item.isRender === 'boolean') {
                        if (item.isRender === false || item.isRender({ row, column, index }) === false) {
                            return true;
                        }
                    }
                };
                options = option.map((item, itemIndex) => {
                    // options添加自定义isRender属性
                    if (optionIsNotRender(item)) return null;

                    return (
                        <>
                            {item.type === 'text' ? (
                                item.name
                            ) : item.type === 'dropdown' ? (
                                <ElDropdown class="zw-table-dropdown">
                                    {{
                                        default: () => <ElButton size="small">更多操作</ElButton>,
                                        dropdown: () => {
                                            return (
                                                <ElDropdownMenu>
                                                    {item.menu.map((menuItem: any, menuIndex: number) => {
                                                        if (optionIsNotRender(menuItem)) return null;
                                                        return (
                                                            <ElDropdownItem
                                                                onClick={() => menuItem.click(row, column, index)}
                                                                v-auth={menuItem.authCode}>
                                                                {menuItem.name}
                                                            </ElDropdownItem>
                                                        );
                                                    })}
                                                </ElDropdownMenu>
                                            );
                                        }
                                    }}
                                </ElDropdown>
                            ) : (
                                <ElButton
                                    v-auth={item.authCode}
                                    disabled={item.disabledFn && item.disabledFn(row)}
                                    size="small"
                                    onClick={() => item.click(row, column, index)}
                                    key={`${item.id}-${itemIndex}`}>
                                    {item.name}
                                </ElButton>
                            )}
                        </>
                    );
                });
            }

            return <div class={bem('btn-box')}>{options}</div>;
        };

        // 渲染 table column
        const renderTableColumn = (list: any) => {
            return list.map((item: any, itemIndex: number) => {
                const columnSlots = {
                    default:
                        (item.render
                            ? ({ row, column, $index }: any) =>
                                item.render?.({
                                    row,
                                    column,
                                    index: $index
                                })
                            : item.isTag
                                ? ({ row, column, $index }: any) =>
                                    renderTags({
                                        row,
                                        column,
                                        index: $index,
                                        prop: item.prop
                                    })
                                : item.showOverflowTooltip
                                    ? ({ row, column, $index }: any) =>
                                        renderShowOverflowTooltip({
                                            row,
                                            column,
                                            index: $index,
                                            item
                                        })
                                    : item.isOperate
                                        ? ({ row, column, $index }: any) =>
                                            renderOperate({
                                                row,
                                                column,
                                                index: $index,
                                                option: item.option,
                                                renderOperate: item.renderOperate
                                            })
                                        : item.isRadio
                                            ? ({ row, column, $index }: any) =>
                                                renderRadio({
                                                    row,
                                                    column,
                                                    index: $index,
                                                    option: item.option,
                                                    renderRadio: item.renderRadio
                                                })
                                            : null) ??
                        (slots[item.slotName]
                            ? ({ row, column, $index }: any) =>
                                slots[item.slotName]?.({
                                    row,
                                    column,
                                    index: $index
                                })
                            : null)
                };
                const header = {
                    default: item.header
                        ? ({ row, column, $index }: any) => {
                            item.renderHeader?.({
                                row,
                                column,
                                index: $index
                            });
                        }
                        : null
                };
                // 过滤el-table默认自带的showOverflowTooltip字段，目前2.2.28～2.2.31不支持两行tootip，只支持但行tooltop。因此失效，所以进行过滤
                const { showOverflowTooltip, ...attrs } = item;
                return Array.isArray(item.table) && item.table.length > 0 ? (
                    <ElTableColumn
                        vSlots={{ columnSlots: columnSlots, header: header }}
                        className={`${item.showOverflowTooltip ? bem('tow-line') : ''}`}
                        {...attrs}
                        key={`${item.prop ?? ''}-${itemIndex}`}>
                        {renderTableColumn(item.table)}
                    </ElTableColumn>
                ) : (
                    <ElTableColumn
                        vSlots={columnSlots}
                        className={`${item.showOverflowTooltip ? bem('tow-line') : ''}`}
                        {...attrs}
                        key={`${item.prop ?? ''}-${itemIndex}`}
                    />
                );
            });
        };

        const maxHeight = computed(() => {
            if (props.isLimitHeight) {
                return 'undefined';
            } else {
                return props.tabH || state.tabH;
            }
        });

        const handleCheckAllChange = (val: any) => {
            if (val.length >= 1) {
                isSelectAll.value = true;
            } else {
                isSelectAll.value = false;
                state.reverseSelectionList = [];
                emit('selectAllChange', state.reverseSelectionList);
            }
        };

        return () => (
            <div class={bem()}>
                {renderHeaderOperate()}
                <ElTable
                    class={bem('com_table')}
                    // maxHeight={props.tabH || state.tabH || 'undefined'}
                    // style={{ width: '100%', maxHeight: props.tabH || state.tabH }}
                    maxHeight={maxHeight.value}
                    style={{ width: '100%', maxHeight: maxHeight.value }}
                    key={randomKey}
                    vSlots={{
                        empty: () =>
                            slots.empty?.() || (
                                <div class={bem('empty')}>
                                    <img class={bem('empty-img')} src={emptyImage} />
                                    <div class={bem('empty-txt')}>{props.emptyText}</div>
                                </div>
                            )
                    }}
                    rowKey={props.rowKey}
                    ref={tableRef}
                    data={state.list}
                    tooltipEffect={'light'}
                    stripe
                    onSelect={onSelect}
                    onSelectionChange={onSelectionChange}
                    onSelectAll={onSelectAll}
                    onCellMouseEnter={onCellMouseEnter}
                    onCellMouseLeave={onCellMouseLeave}
                    onSortChange={onSortChange}>
                    {renderTableColumn(props.option)}
                </ElTable>
                {props.showPagination && (
                    <ElPagination
                        vModel:currentPage={state.page}
                        vModel:pageSize={state.page_size}
                        layout="total, prev, pager, next, sizes"
                        pageSizes={props.pageSizes}
                        total={state.total}
                        background
                        onCurrentChange={onCurrentChange}
                        onSizeChange={onSizeChange}
                    />
                )}
            </div>
        );
    }
});
